#include "serverSideGame.h"

#include "../shape/ogreShape.h"
#include "../client/serverSideClient.h"
#include "../player/serverSidePlayer.h"


#include <fstream>
#include <math.h>
#include <malloc.h>
#include <stdlib.h>

#ifdef WIN32
#include <windows.h>
#include <mmsystem.h>
#include <assert.h>
#endif

ServerSideGame::ServerSideGame()
{

#ifdef _DEBUG
	mRoot = new Ogre::Root("plugins_d.cfg");
#else
	mRoot = new Ogre::Root("plugins.cfg");
#endif

	/*you could put dreamServer constructor stuff right here*/
	init			= false;

	port			= 0;
	runningIndex	= 1;
	socket			= 0;
	clientList		= NULL;

/* end dreamServer constructor*/
	mRealTime	= 0;
	mServerTime	= 0;
	mFramenum	= 0;


}

ServerSideGame::~ServerSideGame()
{
	Client *list = clientList;
	Client *next;

	while(list != NULL)
	{
		next = list->next;

		if(list)
		{
			free(list);
		}

		list = next;
	}

	clientList = NULL;

	dreamSock_CloseSocket(socket);

}

int ServerSideGame::InitNetwork()
{
	if(dreamSock_Initialize() != 0)
	{
		LogString("Error initialising Communication Library!");
		return 1;
	}

	LogString("Initialising game");

	// Create the game servers on new ports, starting from 30004
	int ret = Initialise("", 30004);

	if(ret == DREAMSOCK_SERVER_ERROR)
	{
#ifdef WIN32
		char text[64];
		sprintf(text, "Could not open server on port %d", GetPort());

		MessageBox(NULL, text, "Error", MB_OK);
#else
		LogString("Could not open server on port %d", mNetworkServer->GetPort());
#endif
		return 1;
	}

	return 0;
}

void ServerSideGame::ShutdownNetwork(void)
{
	RemoveClients();
	Uninitialise();
}

void ServerSideGame::AddClient(void)
{
	LogString("about to create player");

	ServerSideClient* serverSideClient = new ServerSideClient();

	memcpy(&serverSideClient->address,serverSideClient->GetSocketAddress(), sizeof(struct sockaddr));

	mClientVector.push_back(serverSideClient);
	
	OgreShape* shape = new OgreShape("jay" + mClientVector.size(),new Vector3D(),mRoot);
	serverSideClient->mPlayer = new ServerSidePlayer("jay" + mClientVector.size(),serverSideClient,shape);

	mNetworkServer->SendAddClient(serverSideClient);
}

void ServerSideGame::RemoveClient(ServerSideClient* client)
{
}

void ServerSideGame::RemoveClients(void)
{
	mClientVector.empty();
}

void ServerSideGame::Frame(int msec)
{
	mRealTime += msec;

	// Read packets from clients
	ReadPackets();

	// Wait full 100 ms before allowing to send
	if(mRealTime < mServerTime)
	{
		// never let the time get too far off
		if(mServerTime - mRealTime > 32)
		{
			mRealTime = mServerTime - 32;
		}

		return;
	}

	// Bump frame number, and calculate new mServerTime
	mFramenum++;
	mServerTime = mFramenum * 32;

	if(mServerTime < mRealTime)
		mRealTime = mServerTime;

	SendCommand();
}

void ServerSideGame::ReadPackets(void)
{
	char data[1400];

	int type;
	int ret;

	struct sockaddr address;

	ServerSideClient *clList;

	dreamMessage mes;
	mes.Init(data, sizeof(data));

	// Get the packet from the socket
	try
	{
		while(ret = mNetworkServer->GetPacket(mes.data, &address))
		{
			mes.SetSize(ret);
			mes.BeginReading();

			type = mes.ReadByte();

			// Check the type of the message
			switch(type)
			{
			case DREAMSOCK_MES_CONNECT:
				AddClient();
				break;

			case DREAMSOCK_MES_DISCONNECT:

				for (unsigned int i = 0; i < mClientVector.size(); i++)
				{
					if(memcmp(&mClientVector.at(i)->address, &address, sizeof(address)) == 0)
					{
						//mClientVector.erase(
					}
				}
				break;

			case USER_MES_FRAME:
//			LogString("Got frame (size: %d bytes)", ret);

				// Skip sequences
				mes.ReadShort();
				mes.ReadShort();

				for (unsigned int i = 0; i < mClientVector.size(); i++)
				{
					if(memcmp(&mClientVector.at(i)->address, &address, sizeof(address)) == 0)
					{
						ReadDeltaMoveCommand(&mes, mClientVector.at(i));
						mClientVector.at(i)->mPlayer->processTick();

						break;
					}
				}

				break;

			case USER_MES_NONDELTAFRAME:

				for (unsigned int i = 0; i < mClientVector.size(); i++)
				{
					mClientVector.at(i)->message.Init(mClientVector.at(i)->message.outgoingData,
						sizeof(clList->message.outgoingData));

					mClientVector.at(i)->message.WriteByte(USER_MES_NONDELTAFRAME);
					mClientVector.at(i)->message.WriteShort(mClientVector.at(i)->GetOutgoingSequence());
					mClientVector.at(i)->message.WriteShort(mClientVector.at(i)->GetIncomingSequence());

					for (unsigned int j = 0; j < mClientVector.size(); j++)
					{
						BuildMoveCommand(&mClientVector.at(i)->message, mClientVector.at(j));
					}
					mClientVector.at(i)->SendPacket();
				}

				break;

			}
		}
	}
	catch(...)
	{
		LogString("Unknown Exception caught in Lobby ReadPackets loop");

#ifdef WIN32
		MessageBox(NULL, "Unknown Exception caught in Lobby ReadPackets loop", "Error", MB_OK | MB_TASKMODAL);
#endif
	}
}

void ServerSideGame::SendCommand(void)
{
	// Fill messages
	for (unsigned int i = 0; i < mClientVector.size(); i++)
	{
		mClientVector.at(i)->message.Init(mClientVector.at(i)->message.outgoingData,
			sizeof(mClientVector.at(i)->message.outgoingData));

		mClientVector.at(i)->message.WriteByte(USER_MES_FRAME);			// type
		mClientVector.at(i)->message.AddSequences(mClientVector.at(i));	// sequences

		for (unsigned int j = 0; j < mClientVector.size(); j++)
		{
			BuildDeltaMoveCommand(&mClientVector.at(i)->message, mClientVector.at(j));
		}
	}

	// Send messages to all clients
	mNetworkServer->SendPackets();

	// Store the sent command in history
	for (unsigned int i = 0; i < mClientVector.size(); i++)
	{
		int num = (mClientVector.at(i)->GetOutgoingSequence() - 1) & (COMMAND_HISTORY_SIZE-1);
		memcpy(&mClientVector.at(i)->mFrame[num], &mClientVector.at(i)->mCommand, sizeof(ServerSideCommand));
	}
}

void ServerSideGame::SendExitNotification(void)
{
	for (unsigned int i = 0; i < mClientVector.size(); i++)
	{
		mClientVector.at(i)->message.Init(mClientVector.at(i)->message.outgoingData,
			sizeof(mClientVector.at(i)->message.outgoingData));

		mClientVector.at(i)->message.WriteByte(USER_MES_SERVEREXIT);	// type
		mClientVector.at(i)->message.AddSequences(mClientVector.at(i));	// sequences
	}

	mNetworkServer->SendPackets();
}

void ServerSideGame::ReadDeltaMoveCommand(dreamMessage *mes, ServerSideClient *client)
{
	int flags = 0;

	// Flags
	flags = mes->ReadByte();

	// Key
	if(flags & CMD_KEY)
	{
		client->mCommand.mKey = mes->ReadByte();

		//LogString("Client %d: read CMD_KEY (%d)", client->GetIndex(), client->mCommand.mKey);
	}

	// Read time to run command
	client->mCommand.mMilliseconds = mes->ReadByte();
}

void ServerSideGame::BuildMoveCommand(dreamMessage *mes, ServerSideClient *client)
{
	Command* command = &client->mCommand;
	// Add to the message
	// Key
	mes->WriteByte(command->mKey);

	// Origin
	mes->WriteFloat(command->mOrigin.x);
	mes->WriteFloat(command->mOrigin.z);
	mes->WriteFloat(command->mVelocity.x);
	mes->WriteFloat(command->mVelocity.z);

	mes->WriteByte(command->mMilliseconds);
}

void ServerSideGame::BuildDeltaMoveCommand(dreamMessage *mes, ServerSideClient *client)
{

	ServerSidePlayer* player = client->mPlayer;
	Command* command = &client->mCommand;
	int flags = 0;

	int last = (client->GetOutgoingSequence() - 1) & (COMMAND_HISTORY_SIZE-1);

	// Check what needs to be updated
	if(client->mFrame[last].mKey != command->mKey)
	{
		flags |= CMD_KEY;
	}

	if(client->mFrame[last].mOrigin.x != command->mOrigin.x ||
		client->mFrame[last].mOrigin.z != command->mOrigin.z)
	{
		flags |= CMD_ORIGIN;
	}

	// Add to the message
	// Flags
	mes->WriteByte(flags);

	// Key
	if(flags & CMD_KEY)
	{
		mes->WriteByte(command->mKey);
	}

	// Origin
	if(flags & CMD_ORIGIN)
	{
		mes->WriteByte(client->mProcessedFrame & (COMMAND_HISTORY_SIZE-1));
	}

	mes->WriteFloat(command->mOrigin.x);
	mes->WriteFloat(command->mOrigin.z);

	mes->WriteFloat(command->mVelocity.x);
	mes->WriteFloat(command->mVelocity.z);

    mes->WriteFloat(command->mRot.x);
	mes->WriteFloat(command->mRot.z);


	mes->WriteByte(command->mMilliseconds);
}

int ServerSideGame::Initialise(const char *localIP, int serverPort)
{
	// Initialise dreamSock if it is not already initialised
	dreamSock_Initialize();

	// Store the server IP and port for later use
	port = serverPort;

	// Create server socket
	socket = dreamSock_OpenUDPSocket(localIP, port);

	if(socket == DREAMSOCK_INVALID_SOCKET)
	{
		return DREAMSOCK_SERVER_ERROR;
	}

	init = true;

	return 0;
}

void ServerSideGame::Uninitialise(void)
{
	dreamSock_CloseSocket(socket);

	init = false;
}

void ServerSideGame::SendAddClient(Client *newClient)
{
	// Send connection confirmation
	newClient->message.Init(newClient->message.outgoingData,
		sizeof(newClient->message.outgoingData));

	newClient->message.WriteByte(DREAMSOCK_MES_CONNECT);	// type
	newClient->SendPacket();

	// Send 'Add client' message to every client
	Client *client = clientList;

	// First inform the new client of the other clients
	for( ; client != NULL; client = client->next)
	{
		LogString("inform new client of others");
		newClient->message.Init(newClient->message.outgoingData,
			sizeof(newClient->message.outgoingData));

		newClient->message.WriteByte(DREAMSOCK_MES_ADDCLIENT); // type

		if(client == newClient)
		{

			LogString("LOCAL Y");
			newClient->message.WriteByte(1);	// local client
			newClient->message.WriteByte(client->GetIndex());
			newClient->message.WriteString(client->GetName());
		}
		else
		{
						LogString("LOCAL N");
			newClient->message.WriteByte(0);	// not-local client
			newClient->message.WriteByte(client->GetIndex());
			newClient->message.WriteString(client->GetName());
		}

		newClient->SendPacket();
	}

	// Then tell the others about the new client
	for(client = clientList; client != NULL; client = client->next)
	{
		if(client == newClient)
			continue;
		LogString("telling others of new client");
		client->message.Init(client->message.outgoingData,
			sizeof(client->message.outgoingData));

		client->message.WriteByte(DREAMSOCK_MES_ADDCLIENT); // type

		client->message.WriteByte(0);
		client->message.WriteByte(newClient->GetIndex());
		client->message.WriteString(newClient->GetName());

		client->SendPacket();
	}
}

void ServerSideGame::SendRemoveClient(Client *client)
{
	int index = client->GetIndex();

	// Send 'Remove client' message to every client
	Client *list = clientList;

	for( ; list != NULL; list = list->next)
	{
		list->message.Init(list->message.outgoingData,
			sizeof(list->message.outgoingData));

		list->message.WriteByte(DREAMSOCK_MES_REMOVECLIENT);	// type
		list->message.WriteByte(index);							// index
	}

	SendPackets();

	// Send disconnection confirmation
	client->message.Init(client->message.outgoingData,
		sizeof(client->message.outgoingData));

	client->message.WriteByte(DREAMSOCK_MES_DISCONNECT);
	client->SendPacket();
}

void ServerSideGame::SendPing(void)
{
	// Send ping message to every client
	Client *list = clientList;

	for( ; list != NULL; list = list->next)
	{
		list->SendPing();
	}
}

void ServerSideGame::AddClient(struct sockaddr *address, char *name)
{

	mServerSideGame->AddClient();	
/*
	LogString("LIB: Adding client, index %d", runningIndex);

	// No clients yet, adding the first one
	if(clientList == NULL)
	{
		LogString("LIB: Server: Adding first client");

		clientList = (Client *) calloc(1, sizeof(Client));

		clientList->SetSocket(socket);
		clientList->SetSocketAddress(address);

		clientList->SetConnectionState(DREAMSOCK_CONNECTING);
		clientList->SetOutgoingSequence(1);
		clientList->SetIncomingSequence(0);
		clientList->SetIncomingAcknowledged(0);
		clientList->SetIndex(runningIndex);
		clientList->SetName(name);
		clientList->next = NULL;

		newClient = clientList;
	}
	else
	{
		LogString("LIB: Server: Adding another client");

		prev = list;
		list = clientList->next;

		while(list != NULL)
		{
			prev = list;
			list = list->next;
		}

		list = (Client *) calloc(1, sizeof(Client));

		list->SetSocket(socket);
		list->SetSocketAddress(address);

		list->SetConnectionState(DREAMSOCK_CONNECTING);
		list->SetOutgoingSequence(1);
		list->SetIncomingSequence(0);
		list->SetIncomingAcknowledged(0);
		list->SetIndex(runningIndex);
		list->SetName(name);
		list->next = NULL;

		prev->next = list;

		newClient = list;
	}
*/
	runningIndex++;

	//SendAddClient(newClient);
}

void ServerSideGame::RemoveClient(Client *client)
{
	Client *list = NULL;
	Client *prev = NULL;
	Client *next = NULL;

	int index = client->GetIndex();

	LogString("LIB: Removing client with index %d", index);

	SendRemoveClient(client);

	for(list = clientList; list != NULL; list = list->next)
	{
		if(client == list)
		{
			if(prev != NULL)
			{
				prev->next = client->next;
			}

			break;
		}

		prev = list;
	}

	if(client == clientList)
	{
		LogString("LIB: Server: removing first client in list");

		if(list) next = list->next;

		if(client) free(client);
		client = NULL;
		clientList = next;
	}
	else
	{
		LogString("LIB: Server: removing a client");

		if(list) next = list->next;

		if(client) free(client);
		client = next;
	}
}

void ServerSideGame::ParsePacket(dreamMessage *mes, struct sockaddr *address)
{
	mes->BeginReading();
	int type = mes->ReadByte();

	// Find the correct client by comparing addresses
	Client *clList = clientList;

	// If we do not have clients yet, skip to message type checking
	if(clList != NULL )
	{
		for( ; clList != NULL; clList = clList->next)
		{
			if(memcmp(clList->GetSocketAddress(), address, sizeof(address)) == 0)
			{
				break;
			}
		}

		if(clList != NULL)
		{
			clList->SetLastMessageTime(dreamSock_GetCurrentSystemTime());

			// Check if the type is a positive number
			// -> is the packet sequenced
			if(type > 0)
			{
				unsigned short sequence		= mes->ReadShort();
				unsigned short sequenceAck	= mes->ReadShort();

				if(sequence <= clList->GetIncomingSequence())
				{
					LogString("LIB: Server: Sequence mismatch (sequence: %ld <= incoming seq: %ld)",
						sequence, clList->GetIncomingSequence());
				}

				clList->SetDroppedPackets(sequence - clList->GetIncomingSequence() - 1);

				clList->SetIncomingSequence(sequence);
				clList->SetIncomingAcknowledged(sequenceAck);
			}

			// Wait for one message before setting state to connected
			if(clList->GetConnectionState() == DREAMSOCK_CONNECTING)
				clList->SetConnectionState(DREAMSOCK_CONNECTED);
		}
	}

	// Parse through the system messages
	switch(type)
	{
	case DREAMSOCK_MES_CONNECT:
		AddClient(address, mes->ReadString());

		LogString("LIBRARY: Server: a client connected succesfully");
		break;

	case DREAMSOCK_MES_DISCONNECT:
		if(clList == NULL)
			break;

		RemoveClient(clList);

		LogString("LIBRARY: Server: a client disconnected");
		break;

	case DREAMSOCK_MES_PING:
		clList->SetPing(dreamSock_GetCurrentSystemTime() - clList->GetPingSent());
		break;
	}
}

int ServerSideGame::CheckForTimeout(char *data, struct sockaddr *from)
{
	int currentTime = dreamSock_GetCurrentSystemTime();

	Client *clList = clientList;
	Client *next;

	for( ; clList != NULL;)
	{
		next = clList->next;

		// Don't timeout when connecting
		if(clList->GetConnectionState() == DREAMSOCK_CONNECTING)
		{
			clList = next;
			continue;
		}

		// Check if the client has been silent for 30 seconds
		// If yes, assume crashed and remove the client
		if(currentTime - clList->GetLastMessageTime() > 30000)
		{
			LogString("Client timeout, disconnecting (%d - %d = %d)",
				currentTime, clList->GetLastMessageTime(), currentTime - clList->GetLastMessageTime());

			// Build a 'fake' message so the application will also
			// receive notification of a client disconnecting
			dreamMessage mes;
			mes.Init(data, sizeof(data));
			mes.WriteByte(DREAMSOCK_MES_DISCONNECT);

			*(struct sockaddr *) from = *clList->GetSocketAddress();

			RemoveClient(clList);

			return mes.GetSize();
		}

		clList = next;
	}

	return 0;
}

int ServerSideGame::GetPacket(char *data, struct sockaddr *from)
{
	// Check if the server is set up
	if(!socket)
		return 0;

	// Check for timeout
	int timeout = CheckForTimeout(data, from);

	if(timeout)
		return timeout;

	// Wait for a while or incoming data
	int maxfd = socket;
	fd_set allset;
	struct timeval waittime;

	waittime.tv_sec = 10 / 1000;
	waittime.tv_usec = (10 % 1000) * 1000;

	FD_ZERO(&allset); 
	FD_SET(socket, &allset);

	fd_set reading = allset;

	int nready = select(maxfd + 1, &reading, NULL, NULL, &waittime);

	if(!nready)
		return 0;

	// Read data of the socket
	int ret = 0;

	dreamMessage mes;
	mes.Init(data, sizeof(data));

	ret = dreamSock_GetPacket(socket, mes.data, from);

	if(ret <= 0)
		return 0;

	mes.SetSize(ret);

	// Parse system messages
	ParsePacket(&mes, from);

	return ret;
}

void ServerSideGame::SendPackets(void)
{
	// Check if the server is set up
	if(!socket)
		return;

	Client *clList = clientList;

	for( ; clList != NULL; clList = clList->next)
	{
		if(clList->message.GetSize() == 0)
			continue;

		clList->SendPacket();
	}
}
